package com.tool.app.utils;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.plugin.table.LoopColumnTableRenderPolicy;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.policy.DocxRenderPolicy;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.deepoove.poi.policy.RenderPolicy;
import org.ddr.poi.html.HtmlRenderPolicy;
import org.springframework.core.io.ClassPathResource;

import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class PoiEIUtil {
	// 常量定义
	public static final String CONST_HTML_KEY_PREFIX = "html_";
	public static final String CONST_LOOP_ROW_KEY_PREFIX = "loopRow_";
	public static final String CONST_SAME_LOOP_ROW_KEY_PREFIX = "sameLoopRow_";
	public static final String CONST_LOOP_COL_KEY_PREFIX = "loopCol_";
	public static final String CONST_IMAGE_KEY_PREFIX = "img_";
	public static final String CONST_DOCX_KEY_PREFIX = "docx_";

	// 策略缓存
	private static final RenderPolicy HTML_POLICY = new HtmlRenderPolicy();
	private static final RenderPolicy LOOP_ROW_POLICY = new LoopRowTableRenderPolicy();
	private static final RenderPolicy SAME_LOOP_ROW_POLICY = new LoopRowTableRenderPolicy(true);
	private static final RenderPolicy LOOP_COL_POLICY = new LoopColumnTableRenderPolicy();

	public static XWPFTemplate buildWordTemplate(String resourceFilePath, HashMap<String, Object> dataMap) {

		XWPFTemplate docTemplate = null;
		try (InputStream in = new ClassPathResource(resourceFilePath).getInputStream()) {
			if (in.available() != 0) {
				ConfigureBuilder configureBuilder = Configure.builder();
				configureBuilder.useSpringEL();
				Set<String> keys = extractTemplateKeys(dataMap);
				// key去重
				HashSet set = new HashSet(keys);
				keys.clear();
				keys.addAll(set);
				buildConfigureByKeys(configureBuilder, keys);
				docTemplate = XWPFTemplate.compile(in, configureBuilder.build()).render(dataMap);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return docTemplate;
	}

	/**
	 * 根据键名模式自动配置渲染策略
	 * @param builder ConfigureBuilder实例
	 * @param keys 模板变量键集合
	 */
	public static void buildConfigureByKeys(ConfigureBuilder builder, Set<String> keys) {
		if (builder == null || keys == null || keys.isEmpty()) {
			return;
		}

		keys.forEach(key -> {
			if (key == null || key.isEmpty()) {
				return;
			}

			// 根据键名前缀匹配策略
			if (key.startsWith(CONST_HTML_KEY_PREFIX)) {
				builder.bind(key, HTML_POLICY);
			}
			else if (key.startsWith(CONST_LOOP_ROW_KEY_PREFIX)) {
				builder.bind(key, LOOP_ROW_POLICY);
			}
			else if (key.startsWith(CONST_SAME_LOOP_ROW_KEY_PREFIX)) {
				builder.bind(key, SAME_LOOP_ROW_POLICY);
			}
			else if (key.startsWith(CONST_LOOP_COL_KEY_PREFIX)) {
				builder.bind(key, LOOP_COL_POLICY);
			}
			else if (key.startsWith(CONST_IMAGE_KEY_PREFIX)) {
				builder.bind(key, new PictureRenderPolicy());
			}
			else if (key.startsWith(CONST_DOCX_KEY_PREFIX)) {
				builder.bind(key, new DocxRenderPolicy());
			}
			// 可以继续添加其他策略...
		});
	}


	/**
	 * 带自定义策略的配置方法
	 * @param builder ConfigureBuilder实例
	 * @param keys 模板变量键集合
	 * @param customPolicies 自定义策略映射
	 */
	public static void buildConfigureByKeys(ConfigureBuilder builder,
	                                        Set<String> keys,
	                                        Map<String, RenderPolicy> customPolicies) {
		// 先应用默认配置
		buildConfigureByKeys(builder, keys);

		// 再应用自定义策略
		if (customPolicies != null && !customPolicies.isEmpty()) {
			customPolicies.forEach((pattern, policy) -> {
				keys.stream()
						.filter(key -> key.matches(pattern))
						.forEach(key -> builder.bind(key, policy));
			});
		}
	}

	/**
	 * 从数据Map中提取所有可能的模板变量
	 */
	private static Set<String> extractTemplateKeys(Map<String, Object> dataMap) {
		Set<String> keys = new HashSet<>();
		dataMap.forEach((key, value) -> {
			keys.add(key);
			// 如果是嵌套对象，提取嵌套属性
			if (value instanceof Map) {
				((Map<?, ?>) value).keySet().forEach(subKey ->
						keys.add(key + "." + subKey)
				);
			}
		});
		return keys;
	}

}
